package org.notabug.lifeuser.moviedb.activity;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import com.squareup.picasso.Picasso;

import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONArray;
import org.notabug.lifeuser.moviedb.ConfigHelper;
import org.notabug.lifeuser.moviedb.NotifyingScrollView;
import org.notabug.lifeuser.moviedb.R;
import org.notabug.lifeuser.moviedb.adapter.SimilarMovieBaseAdapter;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import java.util.ArrayList;

/**
* This class displays information about person objects.
*/
public class CastActivity extends BaseActivity {

	private RecyclerView castMovieView;
	private SimilarMovieBaseAdapter castMovieAdapter;
	private ArrayList<JSONObject> castMovieArrayList;

	private RecyclerView crewMovieView;
	private SimilarMovieBaseAdapter crewMovieAdapter;
	private ArrayList<JSONObject> crewMovieArrayList;

	private Drawable mToolbarBackgroundDrawable;
	private Toolbar toolbar;

	private int actorId;
	private String actorImageId;

	private ImageView actorImage;
	private TextView actorName;
	private ImageView actorIcon;
	private TextView actorPlaceOfBirth;
	private TextView actorBirthday;
	private TextView actorBiography;

	private Activity mActivity;

	private SharedPreferences collapseViewPreferences;
	private SharedPreferences.Editor collapseViewEditor;
	private final static String COLLAPSE_VIEW = "collapseView";
	private final static String CAST_MOVIE_VIEW_PREFERENCE = "CastActivity.castMovieView";
	private final static String CREW_MOVIE_VIEW_PREFERENCE = "CastActivity.crewMovieView";

	/*
	* This class provides an overview for actors.
	* The data is send via an Intent that comes from the DetailActivity
	* DetailActivity in his turn gets the data from the CastBaseAdapter
	*/

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

        // Set the proper layout.
		setContentView(R.layout.activity_cast);
		setNavigationDrawer();
		setBackButtons();

		// Set a transparent background for the toolbar that 
        // becomes less transparent when scrolling down.
		mToolbarBackgroundDrawable = new ColorDrawable(ContextCompat
				.getColor(getApplicationContext(), R.color.colorPrimary));
		mToolbarBackgroundDrawable.setAlpha(0);

		toolbar = (Toolbar) findViewById(R.id.toolbar);
		toolbar.setBackgroundDrawable(mToolbarBackgroundDrawable);

		// Make the transparency dependent on how far the user scrolled down.
		NotifyingScrollView notifyingScrollView = (NotifyingScrollView)
				findViewById(R.id.castScrollView);
		notifyingScrollView.setOnScrollChangedListener(mOnScrollChangedListener);

		if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
			mToolbarBackgroundDrawable.setCallback(drawableCallback);
		}

        // Create a variable with the application context that can be used
        // when data is retrieved.
        mActivity = this;

		// RecyclerView to display the shows that the person was part of the cast in.
		castMovieView = (RecyclerView) findViewById(R.id.castMovieRecyclerView);
		castMovieView.setHasFixedSize(true); // Improves performance (if size is static)

		// RecyclerView to display the movies that the person was part of the crew in.
		crewMovieView = (RecyclerView) findViewById(R.id.crewMovieRecyclerView);
		crewMovieView.setHasFixedSize(true);

		LinearLayoutManager castLinearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
		castMovieView.setLayoutManager(castLinearLayoutManager);

        // RecyclerView to display the shows that the person was part of the crew in.
        crewMovieView = (RecyclerView) findViewById(R.id.crewMovieRecyclerView);
        crewMovieView.setHasFixedSize(true); // Improves performance (if size is static)

        LinearLayoutManager crewLinearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
        crewMovieView.setLayoutManager(crewLinearLayoutManager);

		// Make the views invisible if the user collapsed the view.
		collapseViewPreferences = getApplicationContext()
				.getSharedPreferences(COLLAPSE_VIEW, Context.MODE_PRIVATE);

		if(collapseViewPreferences.getBoolean(CAST_MOVIE_VIEW_PREFERENCE, false)) {
			castMovieView.setVisibility(View.GONE);
		}

		if(collapseViewPreferences.getBoolean(CREW_MOVIE_VIEW_PREFERENCE, false)) {
			crewMovieView.setVisibility(View.GONE);
		}

        // Get the actorObject from the intent that contains the necessary
        // data to display the right person and related RecyclerViews. 
        // Send the JSONObject to setActorData() so all the data 
        // will be displayed on the screen.
		Intent intent = getIntent();
		try {
			setActorData(new JSONObject(intent.getStringExtra(("actorObject"))));

            // Set the adapter with the (still) empty ArrayList.
			castMovieArrayList = new ArrayList<JSONObject>();
			castMovieAdapter = new SimilarMovieBaseAdapter(castMovieArrayList,
					getApplicationContext());
			castMovieView.setAdapter(castMovieAdapter);

            // Set the adapter with the (still) empty ArrayList.
            crewMovieArrayList = new ArrayList<JSONObject>();
            crewMovieAdapter = new SimilarMovieBaseAdapter(crewMovieArrayList,
                    getApplicationContext());
            crewMovieView.setAdapter(crewMovieAdapter);
		} catch(JSONException e) {
			e.printStackTrace();
		}

		// Get the shows the person was a part of.
		new ActorMovieList().execute();

		// Load person details
		new ActorDetails().execute();

		// Check for clicks to collapse/expand the views.
		TextView castMovieTitle = (TextView) findViewById(R.id.castMovieTitle);
		TextView crewMovieTitle = (TextView) findViewById(R.id.crewMovieTitle);

		// Initialise the editor to be able to change preferences in setTitleClickListener.
		collapseViewEditor = collapseViewPreferences.edit();

		// Set a listener to change the visibility when the TextView is clicked.
		setTitleClickListener(castMovieTitle, castMovieView, CAST_MOVIE_VIEW_PREFERENCE);
		setTitleClickListener(crewMovieTitle, crewMovieView, CREW_MOVIE_VIEW_PREFERENCE);
	}

	/**
	 * Sets an OnClickListener on the title above the RecyclerView.
	 * collapseViewEditor needs to be initialised.
	 * @param title the title that the OnClickListener will be set on.
	 * @param view the view that will be expanded/collapsed.
	 * @param preference the preferences that needs to be edited to remember the choice.
	 */
	private void setTitleClickListener(final TextView title, final RecyclerView view, final String preference) {
		title.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				if(collapseViewPreferences.getBoolean(preference, false)) {
					// The view needs to expand.
					view.setVisibility(View.VISIBLE);

					// The preference needs to change.
					collapseViewEditor.putBoolean(preference, false);
				} else {
					// The view needs to collapse.
					view.setVisibility(View.GONE);

					// The preference needs to change.
					collapseViewEditor.putBoolean(preference, true);
				}
				// Set the changes.
				collapseViewEditor.commit();
			}
		});
	}

    /**
    * Sets the data gotten from the actorObject to the appropriate views.
    * @param actorObject the JSONObject that it takes the data from.
    */
	private void setActorData(JSONObject actorObject) {

		// Get the views from the layout
		actorImage = (ImageView) findViewById(R.id.actorImage);
		actorName = (TextView) findViewById(R.id.actorName);
		actorIcon = (ImageView) findViewById(R.id.actorIcon);
		actorPlaceOfBirth = (TextView) findViewById(R.id.actorPlaceOfBirth);
		actorBirthday = (TextView) findViewById(R.id.actorBirthday);
		actorBiography = (TextView) findViewById(R.id.actorBiography);

		// Check if actorObject values differ from the current values,
		// if they do, use the actorObject values (as they are probably
        // more recent).
		try {
			// Set the actorId
			if(actorObject.has("id")) {
				actorId = Integer.parseInt(actorObject.getString("id"));
			}

			// Due to the difficulty of comparing images (or rather,
			// this can be a slow process) the id of the image is
			// saved as class variable for easy comparison.
			if(actorObject.has("profile_path") &&
					!actorObject.getString("profile_path").equals(actorImageId)) {

                // Load the images into the appropriate view.
				Picasso.with(this).load("https://image.tmdb.org/t/p/h632" +
						actorObject.getString("profile_path"))
						.into(actorImage);
				Picasso.with(this).load("https://image.tmdb.org/t/p/w154" +
						actorObject.getString("profile_path"))
						.into(actorIcon);

				// Set the old imageId to the new one.
				actorImageId = actorObject.getString("profile_path");
			}

			// If the name is different in the new dataset, change it.
			if(actorObject.has("name") && !actorObject.getString("name").equals(actorName.getText().toString())) {
				actorName.setText(actorObject.getString("name"));
			}

			// If the place of birth is different in the new dataset, change it.
			if(actorObject.has("place_of_birth") && !actorObject.getString("place_of_birth").equals(actorPlaceOfBirth
					.getText().toString())) {
				actorPlaceOfBirth.setText(getString(R.string.place_of_birth) + actorObject.getString("place_of_birth"));
			}

			// If the birthday is different in the new dataset, change it.
			if(actorObject.has("birthday") && !actorObject.getString("birthday").equals(actorBirthday
					.getText().toString())) {
				actorBirthday.setText(getString(R.string.birthday) + actorObject.getString("birthday"));
			}

			// If the biography is different in the new dataset, change it.
			if(actorObject.has("biography") &&
					!actorObject.getString("biography").equals(
							actorBiography.getText().toString())) {
				actorBiography.setText(actorObject.getString("biography"));
			}
			if(actorObject.getString("biography").equals("")) {
				new ActorDetails().execute("true");
			}
		} catch(JSONException e) {
			e.printStackTrace();
		}
	}

	// Change transparency when scrolling.
	private NotifyingScrollView.OnScrollChangedListener
			mOnScrollChangedListener = new NotifyingScrollView
			.OnScrollChangedListener() {
		public void onScrollChanged(ScrollView scrollView, int l, int t, int oldl, int oldt) {
			final int headerHeight = findViewById(R.id.actorImage).getHeight() -
					toolbar.getHeight();
			final float ratio = (float) Math.min(Math.max(t, 0), headerHeight) / headerHeight;
			final int newAlpha = (int) (ratio * 255);
			mToolbarBackgroundDrawable.setAlpha(newAlpha);
		}
	};

	// Old way to set the background.
	private Drawable.Callback drawableCallback = new Drawable.Callback() {
		@Override
		public void invalidateDrawable(Drawable drawable) {
			toolbar.setBackgroundDrawable(drawable);
		}

		@Override
		public void scheduleDrawable(Drawable drawable, Runnable runnable, long when) {}

		@Override
		public void unscheduleDrawable(Drawable drawable, Runnable runnable) {}
	};

    /**
     * AsyncTask that retrieves the shows that the person is credited
     * for from the API.
     */
	private class ActorMovieList extends AsyncTask<String, Void, String> {

		private final String API_KEY = ConfigHelper.getConfigValue(getApplicationContext(), "api_key");

		protected String doInBackground(String... params) {
			String line;
			StringBuilder stringBuilder = new StringBuilder();

			// Load the webpage with the person's shows.
			try {
				URL url = new URL("https://api.themoviedb.org/3/person/" +
						actorId +"/combined_credits?api_key=" + API_KEY + getLanguageParameter());
				URLConnection urlConnection = url.openConnection();

				try {
					BufferedReader bufferedReader = new BufferedReader(
							new InputStreamReader(
									urlConnection.getInputStream()));

					// Create one long string of the webpage.
					while((line = bufferedReader.readLine()) != null) {
						stringBuilder.append(line).append("\n");
					}

					// Close connection and return the data from the webpage.
					bufferedReader.close();
					return stringBuilder.toString();
				} catch(IOException ioe) {
					ioe.printStackTrace();
				}
			} catch(MalformedURLException mue) {
				mue.printStackTrace();
			} catch(IOException ioe) {
				ioe.printStackTrace();
			}

			// Loading the dataset failed, return null.
			return null;
		}

		protected void onPostExecute(String response) {
			if (response != null && !response.isEmpty()) {
				// Break the JSON dataset down and add the JSONObjects to the array.
				try {
					JSONObject reader = new JSONObject(response);

					// Add the cast roles to the movieView
					if (reader.getJSONArray("cast").length() <= 0) {
						// This person has no roles as cast, do not show the
						// cast related views.
						TextView textView = (TextView)
								mActivity.findViewById(R.id.castMovieTitle);
						View view = mActivity.findViewById(R.id.secondDivider);

						textView.setVisibility(View.GONE);
						view.setVisibility(View.GONE);
						castMovieView.setVisibility(View.GONE);
					} else {
						JSONArray castMovieArray = reader.getJSONArray("cast");
						for(int i = 0; i < castMovieArray.length(); i++) {
							JSONObject actorMovies = castMovieArray.getJSONObject(i);
							castMovieArrayList.add(actorMovies);
						}

						// Set a new adapter so the RecyclerView
						// shows the new items.
						castMovieAdapter = new SimilarMovieBaseAdapter(
								castMovieArrayList, getApplicationContext());
						castMovieView.setAdapter(castMovieAdapter);
					}

					// Add the crew roles to the crewMovieView
					if (reader.getJSONArray("crew").length() <= 0) {
						// This person has no roles as crew, do not show the
						// crew related views.
						TextView textView = (TextView)
								mActivity.findViewById(R.id.crewMovieTitle);
						View view = mActivity.findViewById(R.id.thirdDivider);

						textView.setVisibility(View.GONE);
						view.setVisibility(View.GONE);
						crewMovieView.setVisibility(View.GONE);
					} else {
						JSONArray crewMovieArray = reader.getJSONArray("crew");
						for(int i = 0; i < crewMovieArray.length(); i++) {
							JSONObject crewMovies = crewMovieArray.getJSONObject(i);

							// TODO: Build a lightweight duplicate checker
							// (any heavy ones will cause the application to crash).

							crewMovieArrayList.add(crewMovies);
						}

						// Set a new adapter so the RecyclerView
						// shows the new items.
						crewMovieAdapter = new SimilarMovieBaseAdapter(
								crewMovieArrayList, getApplicationContext());
						crewMovieView.setAdapter(crewMovieAdapter);
					}
				} catch(JSONException je) {
					je.printStackTrace();
				}
			} else {
					Toast.makeText(getApplicationContext(), getApplicationContext().getResources()
							.getString(R.string.no_internet_connection), Toast.LENGTH_SHORT).show();
			}
		}
	}

    /**
     * AsyncTask that retrieves the details of the person from the API.
     */
	private class ActorDetails extends AsyncTask<String, Void, String> {

		private final String API_KEY = ConfigHelper.getConfigValue(getApplicationContext(), "api_key");
		private boolean missingOverview;

		protected String doInBackground(String... params) {
			if (params.length > 0) {
				missingOverview = params[0].equalsIgnoreCase("true");
			}

			String line;
			StringBuilder stringBuilder = new StringBuilder();

			// Load the webpage with the person's details.
			try {
				URL url;
				if (missingOverview) {
					url = new URL("https://api.themoviedb.org/3/person/" +
							actorId + "?api_key=" + API_KEY);
				} else {
					url = new URL("https://api.themoviedb.org/3/person/" +
							actorId + "?api_key=" + API_KEY + getLanguageParameter());
				}
				URLConnection urlConnection = url.openConnection();

				try {
					BufferedReader bufferedReader = new BufferedReader(
							new InputStreamReader(
									urlConnection.getInputStream()));

					// Create one long string of the webpage.
					while((line = bufferedReader.readLine()) != null) {
						stringBuilder.append(line).append("\n");
					}

					// Close connection and return the data from the webpage.
					bufferedReader.close();
					return stringBuilder.toString();
				} catch(IOException ioe) {
					ioe.printStackTrace();
				}
			} catch(MalformedURLException mue) {
				mue.printStackTrace();
			} catch(IOException ioe) {
				ioe.printStackTrace();
			}

			// Loading the dataset failed, return null.
			return null;
		}

		protected void onPostExecute(String response) {
			if (response != null && !response.isEmpty()) {
				// Send all the actor data to setActorData.
				try {
					JSONObject actorData = new JSONObject(response);
					if (missingOverview && actorData.has("biography") && !actorData.getString("biography").equals("")) {
						actorBiography.setText(actorData.getString("biography"));
					} else if (missingOverview && (actorData.optString("biography") == null
							|| (actorData.has("biography") && actorData.get("biography").equals("")))) {
						actorBiography.setText(getString(R.string.no_biography)
								+ actorData.getString("name") + ".");
					} else {
						setActorData(actorData);
					}
				} catch (JSONException e) {
					e.printStackTrace();
				}
			} else {
				Toast.makeText(getApplicationContext(), getApplicationContext().getResources()
						.getString(R.string.no_internet_connection), Toast.LENGTH_SHORT).show();
			}
		}
	}
}
